package br.unb.statistic.distribution;

import java.util.HashMap;

import br.unb.statistic.InterPacketTimeGenerator;
import br.unb.statistic.RandomGenerator;
import br.unb.statistic.inference.Inference;
import br.unb.utils.Resource;

import umontreal.iro.lecuyer.probdist.BetaDist;

public class BetaDistribution extends GenericContinuousDistribution implements
        RandomGenerator, InterPacketTimeGenerator {

    public static final int ALPHA = 0;
    public static final int BETA = 1;
    public static final int LOWER_LIMIT = 2;
    public static final int UPPER_LIMIT = 3;

    public BetaDistribution() {
        paramsCount = 4;
        name = new String("Beta");

        params = new double[paramsCount]; // initialisation du tableau des valeurs des parametres
        paramNames = new String[paramsCount]; // initialisation du tableau des noms des parametres
        params[ALPHA] = 0d;  //0.1d normalement debug 17/01/0.
        paramNames[ALPHA] = new String("alpha");
        params[BETA] = 0d;  //0.1d normalement debug 17/01/0.
        paramNames[BETA] = new String("beta");
        params[LOWER_LIMIT] = 0d;  //0.1d normalement debug 17/01/0.
        paramNames[LOWER_LIMIT] = new String("lower limit");
        params[UPPER_LIMIT] = 0d;  //0.1d normalement debug 17/01/0.
        paramNames[UPPER_LIMIT] = new String("upper limit");
    }

    public BetaDistribution(double alfa, double beta, double lowerLimit, double upperLimit) throws IllegalArgumentException {
        this();
        if (alfa > 0 && beta > 0) {
            setParam(ALPHA, alfa);
            setParam(BETA, beta);
            setParam(LOWER_LIMIT, lowerLimit);
            setParam(UPPER_LIMIT, upperLimit);
        } else {
            throw new IllegalArgumentException(
                    Resource.getString("BETA_PARAMETER_INVALID_LAMBDA"));
        }
    }

    // Ce constructeur n'a pas d'interet sauf celui d'etre compatible avec les autres distributions dans un cas general
    public BetaDistribution(double[] desParams) throws IllegalArgumentException {
        this();
        if (hasValidParam(desParams)) {
            setParams(desParams);
        } else {
            throw new IllegalArgumentException(
                    Resource.getString("BETA_PARAMETER_INVALID_LAMBDA"));
        }
    }

    public boolean hasValidParam(double[] desParams) {
        if (desParams[ALPHA] > 0 && desParams[BETA] > 0) {
            return true;
        } else {
            return false;
        }
    }

    public void estimateParameters(Inference inf) {
        double min = inf.getMin();
        double max = inf.getMax();
        double mediaAlt = (inf.getMean() - min) / (max - min);
        long n = inf.getSampleSize();
        double varianceAlt = (((n - 1) * inf.getVariance()) / n) / Math.pow(max - min, 2);
        double alfa = mediaAlt * ((mediaAlt * (1 - mediaAlt) / varianceAlt) - 1);
        double beta = (1 - mediaAlt) * ((mediaAlt * (1 - mediaAlt) / varianceAlt) - 1);
        setParam(ALPHA, alfa);
        setParam(BETA, beta);
        setParam(LOWER_LIMIT, min);
        setParam(UPPER_LIMIT, max);
    }

    public double pdf(double x) {
        return BetaDist.density(getParam(ALPHA), getParam(BETA), getParam(LOWER_LIMIT), getParam(UPPER_LIMIT), x);
    }

    public double cdf(double x) {
        return BetaDist.cdf(getParam(ALPHA), getParam(BETA), getParam(LOWER_LIMIT), getParam(UPPER_LIMIT), 5, x);
    }

    public double mean() {
        return ((getParam(ALPHA) / (getParam(ALPHA) + getParam(BETA))) * (getParam(UPPER_LIMIT) - getParam(LOWER_LIMIT))) + getParam(LOWER_LIMIT);
    }

    public double var() {
        double alfa = getParam(ALPHA);
        double beta = getParam(BETA);
        return (alfa * beta / (Math.pow(alfa + beta, 2) * (alfa + beta + 1))) * Math.pow(getParam(UPPER_LIMIT) - getParam(LOWER_LIMIT), 2);
    }

    public String getEquation() {
        // TODO Auto-generated method stub (empty)
        return null;
    }

    public double inverseCdf(double x) {
        return BetaDist.inverseF(getParam(ALPHA), getParam(BETA), getParam(LOWER_LIMIT), getParam(UPPER_LIMIT), 5, x);
    }

    public void setParameters(HashMap parameters) {
        double alfa = 0, beta = 0, lowerLimit = 0, upperLimit = 0;
        if (parameters.get("alfa") != null && parameters.get("beta") != null && parameters.get("lowerLimit") != null && parameters.get("upperLimit") != null) {
            alfa = java.lang.Double.parseDouble((String) parameters.get("alfa"));
            beta = java.lang.Double.parseDouble((String) parameters.get("beta"));
            lowerLimit = java.lang.Double.parseDouble((String) parameters.get("lowerLimit"));
            upperLimit = java.lang.Double.parseDouble((String) parameters.get("upperLimit"));
        }
        if (alfa > 0 && beta > 0) {
            setParam(ALPHA, alfa);
            setParam(BETA, beta);
        } 
        if (lowerLimit < upperLimit) {
            setParam(LOWER_LIMIT, lowerLimit);
            setParam(UPPER_LIMIT, upperLimit);
        } 
    }

    public double rand() {
        return inverseCdf(random.nextDouble());
    }

    public double nextInterPacketTime() {
        return rand();
    }

    public void setABParam(double alfa, double beta) {

        if (alfa > 0 && beta > 0) {
            setParam(ALPHA, alfa);
            setParam(BETA, beta);
        } else {
            throw new IllegalArgumentException(
                    Resource.getString("BETA_PARAMETER_INVALID_LAMBDA"));
        }

    }

    public void setParam(int unParam, double uneValeur) throws IllegalArgumentException {
        if (unParam < paramsCount) {
            params[unParam] = uneValeur;
        } else {
            throw new IllegalArgumentException(
                    getName()
                    + Resource.getString("FUNCTION_PARAMETERS_NUMBER")
                    + paramsCount);
        }
    }
    /*
    public static void main(String args[]) {
    BetaDistribution beta = new BetaDistribution(10, 15, -1, 1);
    System.out.println(beta.mean() + " " + beta.var());
    LogFile log = new LogFile("betaTeste");
    for (int i = 0; i < 1000000; i++) {
    log.write("" + beta.rand());
    }
    try {
    Inference inf = new Inference(new DataReader(log.getFile(), "#"));
    System.out.println(inf.getMean() + " " + inf.getVariance());
    log.close();
    BetaDistribution betsdist = new BetaDistribution();
    betsdist.estimateParameters(inf);
    System.out.println(betsdist.getParam(ALPHA) + " " + betsdist.getParam(BETA) + " " + betsdist.getParam(UPPER_LIMIT) + " " + betsdist.getParam(LOWER_LIMIT));
    } catch (IOException e) {
    e.printStackTrace();
    }

    }
     */
}
